package com.songtech.base.universal.dao.api;

import java.io.Serializable;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.stereotype.Repository;

import com.songtech.base.universal.dao.api.beans.TableBeans;
import com.songtech.base.universal.dao.api.util.HandleResult;
import com.songtech.base.universal.dao.api.util.SelectTypeEnum;
import com.songtech.base.universal.dao.beans.BeanParam;
import com.songtech.base.universal.dao.beans.Where;
import com.songtech.base.universal.dao.util.AbstractBaseDao;
import com.songtech.base.universal.dao.util.GetEntityParamUtil;
import com.songtech.base.universal.dao.util.GetReflectionSqlUtil;
import com.songtech.base.universal.dao.util.ThrowsExceptionUtil;
import com.songtech.base.util.SysUUID;

@Repository
public class MapperImpl<T extends Object, PK extends Serializable> extends AbstractBaseDao implements Mapper<T, PK> {
	// 反射工具
	private GetEntityParamUtil getParam;
	
	private String namespace = "com.songtech.base.dao.api.";
	
	public MapperImpl() {
		this.getParam = new GetEntityParamUtil();
	}
	
	/**
	 * 添加不为空的记录（只将不为空字段入库，效率高）
	 * @param obj
	 * @param tableBeans
	 * @return
	 * @throws SQLException 
	 */
	@Override
	public int insert(T obj, TableBeans tableBeans) throws SQLException {
		ThrowsExceptionUtil.throwNullParametersException(tableBeans, obj);// 抛出参数为null的异常
		
		List<BeanParam> list = getParam.getParamByBeanNotEmpty(obj, tableBeans.getEntityClass());
		// 插入时或生成sql时判断是否需要插入主键，所以该位置不判断主键是否为null
		BeanParam column = getParam.getParamById(list);
		// 获取插入单条数据的sql
		Map<String, Object> map = GetReflectionSqlUtil.getInsertSql(list, tableBeans.getTableName(), column);
		ThrowsExceptionUtil.throwMapSizeEq0Exception(map);// 判断map的size是否等于0
		
		return this.getSqlSession().insert(namespace + "insert", map);
	}
	
	/**
	 * 多条数据同时插入
	 * @param objs
	 * @param tableBeans
	 * @return
	 * @throws SQLException 
	 */
	@Override
	public int insert(List<T> objs, TableBeans tableBeans) throws SQLException {
		ThrowsExceptionUtil.throwNullParametersException(tableBeans, objs);// 抛出参数为null的异常
		
		List<List<BeanParam>> list = new ArrayList<>();
		for(T obj : objs){
			List<BeanParam> l = getParam.getParamByBean(obj, tableBeans.getEntityClass());
			list.add(l);
		}
		// 插入时或生成sql时判断是否需要插入主键，所以该位置不判断主键是否为null
		BeanParam column = getParam.getParamById(list.get(0));// 已判断了objs是否为空或0长度，所以者位置可以不对list进行判断
		// 获取批量插入的sql
		Map<String, Object> map = GetReflectionSqlUtil.getInsertsSql(list, tableBeans.getTableName(), column);
		ThrowsExceptionUtil.throwMapSizeEq0Exception(map);// 判断map的size是否等于0
		
		return this.getSqlSession().insert(namespace + "insert", map);
	}
	
	/**
	 * 添加不为空的记录(主键使用系统生成的UUID)
	 * @param obj
	 * @param tableBeans
	 * @return
	 * @throws SQLException 
	 */
	@Override
	public int insertSetUUID(T obj, TableBeans tableBeans) throws SQLException {
		ThrowsExceptionUtil.throwNullParametersException(tableBeans, obj);// 抛出参数为null的异常
		
		List<BeanParam> list = getParam.getParamByBeanNotEmpty(obj, tableBeans.getEntityClass());
		// 设置主键
		getParam.getParamById(list).setValue(SysUUID.generator());
		
		// 获取插入单条数据的sql
		Map<String, Object> map = GetReflectionSqlUtil.getInsertSql(list, tableBeans.getTableName(), null);
		ThrowsExceptionUtil.throwMapSizeEq0Exception(map);// 判断map的size是否等于0
		
		return this.getSqlSession().insert(namespace + "insert", map);
	}
	
	/**
	 * 多条数据同时插入(主键使用系统生成的UUID)
	 * @param objs
	 * @param tableBeans
	 * @return
	 * @throws SQLException 
	 */
	@Override
	public int insertSetUUID(List<T> objs, TableBeans tableBeans) throws SQLException {
		ThrowsExceptionUtil.throwNullParametersException(tableBeans, objs);// 抛出参数为null的异常
		
		List<List<BeanParam>> list = new ArrayList<>();
		for(T obj : objs){
			List<BeanParam> l = getParam.getParamByBean(obj, tableBeans.getEntityClass());
			// 设置主键
			getParam.getParamById(l).setValue(SysUUID.generator());
			list.add(l);
		}
		// 获取批量插入的sql
		Map<String, Object> map = GetReflectionSqlUtil.getInsertsSql(list, tableBeans.getTableName(), null);
		ThrowsExceptionUtil.throwMapSizeEq0Exception(map);// 判断map的size是否等于0
		
		return this.getSqlSession().insert(namespace + "insert", map);
	}
	
	/**
	 * 删除某个记录
	 * @param id 主键
	 * @param tableBeans
	 * @return
	 * @throws SQLException 
	 */
	@Override
	public int delete(PK id, TableBeans tableBeans) throws SQLException {
		ThrowsExceptionUtil.throwNullParametersException(tableBeans, id);// 抛出参数为null的异常
		
		BeanParam column = tableBeans.getColumn(id);
		ThrowsExceptionUtil.throwNullPrimaryKeyException(column);// 抛出空主键异常
		
		return this.delete(new Where(column), tableBeans);
	}
	
	/**
	 * 条件删除记录
	 * @param obj 条件对象
	 * @param tableBeans
	 * @return
	 * @throws SQLException 
	 */
	@Override
	public int delete(T obj, TableBeans tableBeans) throws SQLException {
		ThrowsExceptionUtil.throwNullParametersException(obj);// 抛出参数为null的异常
		return this.delete(new Where(obj), tableBeans);
	}
	
	/**
	 * 条件删除记录
	 * @param where 条件
	 * @param tableBeans
	 * @return 受影响的行数
	 * @throws SQLException 
	 */
	@Override
	public int delete(Where where, TableBeans tableBeans) throws SQLException {
		ThrowsExceptionUtil.throwNullParametersException(tableBeans, where);// 抛出参数为null的异常
		
		Map<String, Object> map = GetReflectionSqlUtil.getDeleteSql(where, tableBeans.getTableName(), tableBeans.getEntityClass());
		ThrowsExceptionUtil.throwMapSizeEq0Exception(map);// 判断map的size是否等于0
		
		return this.getSqlSession().delete(namespace + "delete", map);
	}
	
	/**
	 * 更新不为null的字段
	 * <br>根据主键进行更新
	 * @param obj
	 * @param tableBeans
	 * @return
	 * @throws SQLException 
	 */
	@Override
	public int updateNotEmpty(T obj, TableBeans tableBeans) throws SQLException {
		return this.update(obj, tableBeans, null, SelectTypeEnum.UPDATE_NOT_EMPTY);
	}
	
	/**
	 * 更新所有字段
	 * <br>根据主键进行更新
	 * @param obj
	 * @param tableBeans
	 * @return
	 * @throws SQLException 
	 */
	@Override
	public int update(T obj, TableBeans tableBeans) throws SQLException {
		return this.update(obj, tableBeans, null, SelectTypeEnum.UPDATE_ALL_EMPTY);
	}
	
	/**
	 * 条件更新不为null的字段
	 * @param obj
	 * @param where 条件
	 * @param tableBeans
	 * @return
	 * @throws SQLException 
	 */
	@Override
	public int updateNotEmpty(T obj, T where, TableBeans tableBeans) throws SQLException {
		ThrowsExceptionUtil.throwNullParametersException(where);// 抛出参数为null的异常
		return this.updateNotEmpty(obj, new Where(where), tableBeans);
	}
	
	/**
	 * 条件更新所有字段
	 * @param obj
	 * @param where 条件
	 * @param tableBeans
	 * @return
	 * @throws SQLException 
	 */
	@Override
	public int update(T obj, T where, TableBeans tableBeans) throws SQLException {
		ThrowsExceptionUtil.throwNullParametersException(where);// 抛出参数为null的异常
		return this.update(obj, new Where(where), tableBeans);
	}
	
	/**
	 * 条件更新不为null的字段
	 * @param obj
	 * @param where 条件
	 * @param tableBeans
	 * @return
	 * @throws SQLException 
	 */
	@Override
	public int updateNotEmpty(T obj, Where where, TableBeans tableBeans) throws SQLException {
		ThrowsExceptionUtil.throwNullParametersException(where);// 抛出参数为null的异常
		return this.update(obj, tableBeans, where, SelectTypeEnum.UPDATE_NOT_EMPTY);
	}
	
	/**
	 * 条件更新所有字段
	 * @param obj
	 * @param where 条件
	 * @param tableBeans
	 * @return
	 * @throws SQLException 
	 */
	@Override
	public int update(T obj, Where where, TableBeans tableBeans) throws SQLException {
		ThrowsExceptionUtil.throwNullParametersException(where);// 抛出参数为null的异常
		return this.update(obj, tableBeans, where, SelectTypeEnum.UPDATE_ALL_EMPTY);
	}
	
	/**
	 * 单表的更新
	 * @param obj
	 * @param tableBeans
	 * @param where 判断更新条件Where/BeanParam(主键)
	 * <br>如果传入的where是null，将使用obj的主键作为条件
	 * @param type 查询方式，更新obj中非空的字段或者全部字段
	 * @return
	 * @throws SQLException
	 */
	private int update(T obj, TableBeans tableBeans, Object where, SelectTypeEnum type) throws SQLException{
		ThrowsExceptionUtil.throwNullParametersException(tableBeans, obj);// 抛出参数为null的异常
		
		// 更新方式(非空字段/所有字段)
		List<BeanParam> list;
		if(type == SelectTypeEnum.UPDATE_ALL_EMPTY){
			list = getParam.getParamByBean(obj, tableBeans.getEntityClass());
		}else{
			list = getParam.getParamByBeanNotEmpty(obj, tableBeans.getEntityClass());
		}
		// 如果传入的where是null，将使用obj的主键作为条件
		if(where == null){
			where = getParam.getParamById(list);
			ThrowsExceptionUtil.throwNullPrimaryKeyException((BeanParam) where);// 抛出空主键异常
		}
		
		// 更新条件
		Map<String, Object> map = null;
		// 因传入where默认使用obj的主键作为查询条件(为空时抛出异常)，所以不需要再判断where是否为null
		if(where instanceof Where){// 更新条件是where
			map = GetReflectionSqlUtil.getUpdateByWhereSql(list, tableBeans.getTableName(), (Where)where, tableBeans.getEntityClass());
		}else if(where instanceof BeanParam){// 更新条件是bean(主键)
			map = GetReflectionSqlUtil.getUpdateSql(list, tableBeans.getTableName(), (BeanParam)where, tableBeans.getEntityClass());
		}
		ThrowsExceptionUtil.throwMapSizeEq0Exception(map);// 判断map的size是否等于0
		
		return this.getSqlSession().update(namespace + "update", map);
	}
	
	/**
	 * 通过主键获取某个记录
	 * @param id 主键
	 * @param tableBeans
	 * @return Object
	 * @throws SQLException 
	 */
	@SuppressWarnings("unchecked")
	@Override
	public T selectById(Serializable id, TableBeans tableBeans) throws SQLException {
		List<Map<String, Object>> listM = (List<Map<String, Object>>) this.select(id, tableBeans, SelectTypeEnum.SELECT_ONE_SIZE_ALL_EMPTY);
		// 将查询出来的数据组装为实体list
		return (T) new HandleResult().handleResult(listM, tableBeans.getEntityClass(), tableBeans.getSqlParms());
	}
	
	/**
	 * 通过主键获取某个字段的值
	 * @param id
	 * @param fileName
	 * @param tableBeans
	 * @return
	 * @throws SQLException 
	 */
	@SuppressWarnings("unchecked")
	@Override
	public Object selectFieldById(PK id, String fileName, TableBeans tableBeans) throws SQLException {
		ThrowsExceptionUtil.throwNullParametersException(fileName);
		Map<String, Object> listM = (Map<String, Object>) this.select(id, tableBeans, SelectTypeEnum.SELECT_ONE_SIZE_ONE_EMPTY, fileName);
		
		// 返回查询到的数据
		for (Map.Entry<String, Object> entry : listM.entrySet()) {
			return entry.getValue();
		}
		return null;
	}
	
	/**
	 * 条件查询列表
	 * @param obj 查询的实体类
	 * @param tableBeans
	 * @throws SQLException 
	 */
	@Override
	public List<T> selectList(Object obj, TableBeans tableBeans) throws SQLException {
		ThrowsExceptionUtil.throwNullParametersException(tableBeans, obj);// 抛出参数为null的异常
		return this.selectList(new Where(obj), tableBeans);
	}
	
	/**
	 * 条件查询列表
	 * @param where 条件表达式
	 * @param tableBeans
	 * @throws SQLException 
	 */
	@SuppressWarnings("unchecked")
	@Override
	public List<T> selectList(Where where, TableBeans tableBeans) throws SQLException {
		List<Map<String, Object>> listM = (List<Map<String, Object>>) this.select(where, tableBeans, SelectTypeEnum.SELECT_MORE_ALL_EMPTY);

		return (List<T>) new HandleResult().handleResult(listM, tableBeans.getEntityClass(), tableBeans.getSqlParms());
	}
	
	/**
	 * 查询某个字段列表
	 * @param where 条件
	 * @param fileName 要查询的字段
	 * @param tableBeans
	 * @return
	 * @throws SQLException 
	 */
	@SuppressWarnings("unchecked")
	@Override
	public List<Serializable> selectListFile(Where where, String fileName, TableBeans tableBeans) throws SQLException {
		ThrowsExceptionUtil.throwNullParametersException(fileName);// 抛出参数为null的异常
		
		return (List<Serializable>) this.select(where, tableBeans, SelectTypeEnum.SELECT_MORE_ONE_EMPTY, fileName);
	}
	
	/**
	 * 查询
	 * @param where
	 * @param tableBeans
	 * @param type 查询方式，查询单条数据或多条数据的指定字段或者全部字段
	 * @param fileName 要查询的指定字段(单个字段查询时传入)
	 * @return
	 * @throws SQLException
	 */
	private Object select(Object where, TableBeans tableBeans, SelectTypeEnum type, String... fileName) throws SQLException{
		ThrowsExceptionUtil.throwNullParametersException(tableBeans, where);// 抛出参数为null的异常
		
		// 查询方式(单个字段/所有字段)
		Map<String, Object> map = null;
		
		if(SelectTypeEnum.isSelectOneSize(type)){
			// 根据主键查询一条数据
			ThrowsExceptionUtil.throwNullPrimaryKeyException(tableBeans.getColumn());// 抛出空主键异常
			if(type == SelectTypeEnum.SELECT_ONE_SIZE_ALL_EMPTY){
				// 查询一条数据的所有字段
				map = GetReflectionSqlUtil.getSelectSql(tableBeans.getTableName(), tableBeans.getSqlParms(), tableBeans.getColumn(where));
			}else if(type == SelectTypeEnum.SELECT_ONE_SIZE_ONE_EMPTY){
				// 查询一条数据的单个字段
				if(fileName != null){
					map = GetReflectionSqlUtil.selectFieldById(tableBeans.getTableName(), fileName[0], tableBeans.getColumn(where));
				}
			}
		}else if(SelectTypeEnum.isSelectMore(type)){
			// 查询多条数据
			if(type == SelectTypeEnum.SELECT_MORE_ALL_EMPTY){
				// 查询多条数据的所有字段
				map = GetReflectionSqlUtil.getSelectListSql((Where)where, tableBeans.getTableName(), tableBeans.getSqlParms(), tableBeans.getEntityClass());
			}else if(type == SelectTypeEnum.SELECT_MORE_ONE_EMPTY){
				// 查询多条数据的单个字段
				if(fileName != null){
					map = GetReflectionSqlUtil.getSelectListFileSql((Where)where, tableBeans.getTableName(), fileName[0], tableBeans.getEntityClass());
				}
			}
		}
		ThrowsExceptionUtil.throwMapSizeEq0Exception(map);// 判断map的size是否等于0

		if(SelectTypeEnum.isSelectOneSize(type)){
			return this.getSqlSession().selectOne(namespace + "select", map);
		}else if(SelectTypeEnum.isSelectMore(type)){
			return this.getSqlSession().selectList(namespace + "select", map);
		}
		return null;
	}
	
	/**
	 * 自定义sql查询
	 * @param sql 用于执行查询的Sql
	 * @return 结果集合
	 * @throws SQLException 
	 */
	@Override
	public List<Map<String, Object>> listBySql(String sql) throws SQLException {
		ThrowsExceptionUtil.throwSqlIsNullException(sql);// 抛出传入sql为null的异常
		Map<String, String> map = new HashMap<>();
		map.put("sql", sql);
		return this.getSqlSession().selectList(namespace + "select", map);
	}
	
	/**
	 * 执行自定义sql
	 * @param sql 用于执行的Sql
	 * @return 受影响的行数
	 * @throws SQLException 
	 */
	@Override
	public int excuse(String sql) throws SQLException {
		ThrowsExceptionUtil.throwSqlIsNullException(sql);// 抛出传入sql为null的异常
		Map<String, String> map = new HashMap<>();
		map.put("sql", sql);
		return this.getSqlSession().update(namespace + "select", map);
	}
	
	/**
	 * 获取指定条件的记录数
	 * @param where 条件
	 * @param tableBeans
	 * @return 查询到的记录数
	 * @throws SQLException 
	 */
	@Override
	public long count(Where where, TableBeans tableBeans) throws SQLException {
		ThrowsExceptionUtil.throwNullParametersException(tableBeans, where);// 抛出参数为null的异常
		
		Map<String, Object> map = GetReflectionSqlUtil.getCountSql(where, tableBeans.getTableName(), tableBeans.getEntityClass());
		ThrowsExceptionUtil.throwMapSizeEq0Exception(map);// 判断map的size是否等于0
		
		return this.getSqlSession().selectOne(namespace + "select", map);
	}
	
	/**
	 * 是否存在字段相同的记录（ID以及不为空的字段除外）
	 * @param obj 参照实体
	 * @param tableBeans
	 * @return
	 * @throws SQLException 
	 */
	@Override
	public boolean isExist(T obj, TableBeans tableBeans) throws SQLException {
		ThrowsExceptionUtil.throwNullParametersException(tableBeans, obj);// 抛出参数为null的异常
		return this.count(new Where(obj), tableBeans) > 0;
	}
	
	/**
	 * 是否存在指定条件的记录
	 * @param where 条件
	 * @param tableBeans
	 * @return
	 * @throws SQLException 
	 */
	@Override
	public boolean isExist(Where where, TableBeans tableBeans) throws SQLException {
		return this.count(where, tableBeans) > 0;
	}
	
	/**
	 * 获得下一个序列的值
	 * @return
	 */
	@Override
	public long nextId() {
		return 0;
	}
}
